﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace MyPanel
{
    abstract public class IPanel
    {
        object _eventLock = new object();
        public event Action YChanged
        {
            add
            {
                lock (_eventLock)
                {
                    _yChanged -= value;
                    _yChanged += value;
                }
            }
            remove
            {
                lock (_eventLock)
                {
                    _yChanged -= value;
                }
            }
        }
        Action _yChanged;
        public event Action XBorder
        {
            add
            {
                lock (_eventLock)
                {
                    _xBorder -= value;
                    _xBorder += value;
                }
            }
            remove
            {
                lock (_eventLock)
                {
                    XBorder -= value;
                }
            }
        }
        Action _xBorder;
        public event Action YBorder
        {
            add
            {
                lock (_eventLock)
                {
                    _yBorder -= value;
                    _yBorder += value;
                }
            }
            remove
            {
                lock (_eventLock)
                {
                    _yBorder -= value;
                }
            }
        }
        Action _yBorder;
        public event Action NoItem
        {
            add
            {
                lock (_eventLock)
                {
                    _noItem -= value;
                    _noItem += value;
                }
            }
            remove
            {
                lock (_eventLock)
                {
                    _noItem -= value;
                }
            }
        }
        Action _noItem;

        protected List<Item> items = new List<Item>();
        public abstract string panelName { get; }
        public bool horizontal { get { return _horizontal; } set { _horizontal = value; } }
        protected bool _horizontal = true;

        public virtual int y
        {
            get
            {
                if (horizontal)
                    return pY;
                return pX;
            }
            set
            {
                if (!horizontal)
                    SetX(value);
                else
                    SetY(value);
                _yChanged?.Invoke();
            }
        }
        protected int pY = -1;
        public virtual int x
        {
            get
            {
                if (horizontal)
                    return pX;
                return pY;
            }
            set
            {
                if (!horizontal)
                    SetY(value);
                else
                    SetX(value);
            }
        }
        protected int pX = 0;
        public Cell this[int indexY,int indexX]
        {
            get
            {
                if (indexY > -1 && horizontal)
                    return items[indexY][indexX];
                else if (indexX > -1 && !horizontal)
                    return items[indexX][indexY];
                return null;
            }
            set
            {
                if (indexY > -1 && horizontal)
                    items[indexY][indexX] = value;
                else if (indexX > -1 && !horizontal)
                    items[indexX][indexY] = value;
            }
        }

        void SetY(int value)
        {
            if (items.Count > 0)
            {
                if (value < 0)
                {
                    pY = 0;
                    _yBorder?.Invoke();
                }
                else if (value > items.Count - 1)
                {
                    pY = items.Count - 1;
                    _yBorder?.Invoke();
                }
                else
                    pY = value;

                if (items[pY].Count - 1 < pX)
                    pX = items[pY].Count - 1;
            }
            else
            {
                pY = -1;
                _noItem();
            }
        }
        void SetX(int value)
        {
            if (pY > -1)
            {
                if (value < 0)
                {
                    pX = 0;
                    _xBorder?.Invoke();
                }
                else if (value > items[pY].Count - 1)
                {
                    pX = items[pY].Count - 1;
                    _xBorder?.Invoke();
                }
                else
                    pX = value;
            }
            else
                _noItem?.Invoke();
        }

        public void AddItem(Item item,object tag=null)
        {
            if (tag != null)
                item.tag = tag;
            items.Add(item);
        }

        public void InitIndex()
        {
            pY = -1;
            pX = 0;
        }
    }

    public class Item:IEnumerable<Cell>
    {
        protected Action<object> onClick = null;
        protected List<Cell> cells = new List<Cell>();

        public Cell this[int index]
        {
            get { return cells[index]; }
            set { cells[index] = value; }
        }
        public int Count { get { return cells.Count; } }
        public object tag { get; set; }

        public Item()
        {
        }

        public Item(int cellNum)
        {
            cells = new List<Cell>(cellNum);
        }

        public void AddCell(Cell cell)
        {
            var c = new Cell(cell.text, this, cell.onClick);
            cells.Add(c);
        }

        public void AddCell(string text,Action<object> onClick=null,object tag = null)
        {
            var c = new Cell(text, this, onClick);
            c.tag = tag;
            cells.Add(c);
        }

        public virtual void Click(object obj)
        {
            if (onClick != null)
                onClick(obj);
        }

        public IEnumerator<Cell> GetEnumerator()
        {
            return cells.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return cells.GetEnumerator();
        }
    }

    public class Cell:Item
    {
        public Item parent { get; private set; }
        public string text { get; private set; }
        public new object tag { get; set; }

        public Cell(string text, Item parent = null, Action<object> onClick = null)
        {
            this.text = text;
            this.parent = parent;
            base.onClick = onClick;
            base.cells.Add(this);
        }

        public override void Click(object obj)
        {
            if (onClick == null)
                parent.Click(obj);
            else
                onClick(obj);
        }
    }
}
